﻿//-------------------------------------------------------------------
//	Copyright (c) 2010-2014  Zhirnov Andrey
//	This file is part of the "UX Standard Lib" project.
//	See copyright notice in "UX_STL_Core.h".
//-------------------------------------------------------------------

#ifndef _UXS_HALF_TYPE_H
#define _UXS_HALF_TYPE_H

#pragma once

#include "Types.h"
#include "AllFunc.h"

namespace UX_STL
{
namespace UXTypes
{
	
#	define _HALF_OPERATOR( _op, _ret1, _func1, _ret2, _func2, _ret3, _func3 ) \
									 _ret1 operator _op (const THalf& right) const			_func1 \
		template <typename T>		 _ret2 operator _op (const T& right) const				_func2 \
		template <typename T> friend _ret3 operator _op (const T& left, const THalf& right)	_func3

#	define _HALF_CMP_OPERATOR( _op ) \
		bool operator _op (const THalf& right) const	{ return Get() _op right.Get(); } \
		bool operator _op (const float& right) const	{ return Get() _op right; }




	//
	// Half Float
	//

	struct THalf
	{
		// types
		struct THalfBits
		{
			uint16	m	: 10;	// mantissa
			uint16	e	: 5;	// exponent
			uint16	s	: 1;	// sign
		};


		// variables
		union {
			THalfBits	_bits;
			uint16		_value;
		};


		// constructors
		THalf(): _value(0)								{}
		THalf(const THalf &h): _value(h._value)			{}
	
		template <typename T>
		explicit THalf(const T& val)					{ Set( float(val) ); }


		// set/get
		uint16	GetU() const							{ return _value; }
		uint16&	GetU()									{ return _value; }
		int		GetI() const							{ return int(Get()); }
		float	Get() const;
		void	Set(const float& f);


		// type cast
		operator int () const							{ return GetI(); }
		operator float () const							{ return Get(); }
	

		// unary operators
		THalf &	operator ++ ()							{ ++_bits.m; return *this; }
		THalf &	operator -- ()							{ --_bits.m; return *this; }
		float	operator ++ (int) 						{ float ret(*this);  ++(*this);  return ret; }
		float	operator -- (int)						{ float ret(*this);  --(*this);  return ret; }
		bool	operator !  () const					{ return not Get(); }
		float	operator +  () const					{ return +Get(); }
		float	operator -  () const						{ return -Get(); }
		THalf	operator ~  () const					{ THalf  ret;  ret._value = ~_value;  return ret; }


		// binary operators
		THalf & operator =  (const THalf& h)			{ _value = h._value;  return *this; }

		template <typename T>	THalf & operator =   (const T& right)		{ Set( float(right) );  return *this; }
		template <typename T>	THalf & operator +=  (const T& right)		{ Set( Get() + right );  return *this; }
		template <typename T>	THalf & operator -=  (const T& right)		{ Set( Get() - right );  return *this; }
		template <typename T>	THalf & operator *=  (const T& right)		{ Set( Get() * right );  return *this; }
		template <typename T>	THalf & operator /=  (const T& right)		{ Set( Get() / right );  return *this; }
		template <typename T>	THalf & operator %=  (const T& right)		{ Set( Mod( Get(), right ) );  return *this; }
		template <typename T>	THalf & operator |=  (const T& right)		{ _value |= uint16(right);  return *this; }
		template <typename T>	THalf & operator &=  (const T& right)		{ _value &= uint16(right);  return *this; }
		template <typename T>	THalf & operator ^=  (const T& right)		{ _value ^= uint16(right);  return *this; }
								THalf & operator |=  (const THalf& right)	{ _value |= right.GetU();  return *this; }
								THalf & operator &=  (const THalf& right)	{ _value &= right.GetU();  return *this; }
								THalf & operator ^=  (const THalf& right)	{ _value ^= right.GetU();  return *this; }

#	if 1
		template <typename T>	THalf & operator <<= (const T& right)		{ _bits.e += int8(right);  return *this; }
		template <typename T>	THalf & operator >>= (const T& right)		{ _bits.e -= int8(right);  return *this; }
#	else
		template <typename T>	THalf & operator <<= (const T& right)		{ _value <<= int(right);  return *this; }
		template <typename T>	THalf & operator >>= (const T& right)		{ _value >>= int(right);  return *this; }
#	endif
								THalf & operator <<= (const THalf& right)	{ return ( *this <<= right.GetU() ); }
								THalf & operator >>= (const THalf& right)	{ return ( *this >>= right.GetU() ); }

		_HALF_OPERATOR( +,
				float,	{ return Get() + right.Get(); },
				float,	{ return Get() + float(right); },
				T,		{ return left  + T(right.Get()); }
		);
		_HALF_OPERATOR( -,
				float,	{ return Get() - right.Get(); },
				float,	{ return Get() - float(right); },
				T,		{ return left  - T(right.Get()); }
		);
		_HALF_OPERATOR( *,
				float,	{ return Get() * right.Get(); },
				float,	{ return Get() * float(right); },
				T,		{ return left  * T(right.Get()); }
		);
		_HALF_OPERATOR( /,
				float,	{ return Get() / right.Get(); },
				float,	{ return Get() / float(right); },
				T,		{ return left  / T(right.Get()); }
		)
		_HALF_OPERATOR( %,
				float,	{ return _hidden_::fmod( Get(), right.Get() ); },
				float,	{ return _hidden_::fmod( Get(), float(right) ); },
				T,		{ return _hidden_::fmod( left, T(right.Get()) ); }
		);
		_HALF_OPERATOR( |,
				THalf,	{ return THalf(*this) |= right._value; },
				THalf,	{ return THalf(*this) |= right; },
				T,		{ return left | right.GetU(); }
		);
		_HALF_OPERATOR( &,
				THalf,	{ return THalf(*this) &= right._value; },
				THalf,	{ return THalf(*this) &= right; },
				T,		{ return left & right.GetU(); }
		);
		_HALF_OPERATOR( ^,
				THalf,	{ return THalf(*this) ^= right._value; },
				THalf,	{ return THalf(*this) ^= right; },
				T,		{ return left ^ right.GetU(); }
		);
		_HALF_OPERATOR( <<,
				THalf,	{ return THalf(*this) <<= right._value; },
				THalf,	{ return THalf(*this) <<= right; },
				T,		{ return left << right.GetU(); }
		);
		_HALF_OPERATOR( >>,
				THalf,	{ return THalf(*this) >>= right._value; },
				THalf,	{ return THalf(*this) >>= right; },
				T,		{ return left >> right.GetU(); }
		);

		_HALF_CMP_OPERATOR( == );
		_HALF_CMP_OPERATOR( != );
		_HALF_CMP_OPERATOR( >= );
		_HALF_CMP_OPERATOR( >  );
		_HALF_CMP_OPERATOR( <= );
		_HALF_CMP_OPERATOR( <  );


		// methods
		uint16	GetMantissaBits()	const	{ return _bits.m; }
		int16	GetExponentBits()	const	{ return int16(_bits.e) - (1<<4) + 1; }
		int8	GetSign()			const	{ return _bits.s ? -1 : 1; }

		float	GetMantissa()		const	{ return float(1) + float(_bits.m) / float( (1<<10)-1 ); }
		float	GetExponent()		const;


		// checks //
		STATIC_ASSERT( sizeof(uint16) == 2, "incorrect size" );
	};


	typedef THalf	half_t;
	typedef THalf	float16_t;



/*
=================================================
	возвращает экспоненту
=================================================
*/
	inline float THalf::GetExponent() const
	{
		int16	p = GetExponentBits();
		
		if ( p >= 0 )
			return float( 1 << p );
		else
			return 1.f / float( 1 << (-p) ); 
	}

/*
=================================================
	переводит half в float
=================================================
*/
	inline float THalf::Get() const
	{
#	if 0
		// fast conversion

		float32_t	f;
		f._bits.s = _bits.s;
		f._bits.e = _bits.e + (127 - 15);
		f._bits.m = _bits.m << (23-10);
		return f;
#	else
		// accurate conversion
		// based on code from OpenGL Mathematics (GLM)
		// from http://glm.g-truc.net/

		int s = (_value >> 15) & 0x00000001;
		int e = (_value >> 10) & 0x0000001f;
		int m =  _value        & 0x000003ff;

		if (e == 0)
		{
			if (m == 0)
				return ReferenceCast<float>( s << 31 );
			else
			{
				while (not (m & 0x00000400))
				{
					m <<= 1;
					e -=  1;
				}

				e += 1;
				m &= ~0x00000400;
			}
		}
		else
		if (e == 31)
		{
			if (m == 0)
				return ReferenceCast<float>( (s << 31) | 0x7f800000 );
			else
				return ReferenceCast<float>( (s << 31) | 0x7f800000 | (m << 13) );
		}

		e = e + (127 - 15);
		m = m << 13;

		return ReferenceCast<float>( (s << 31) | (e << 23) | m );
#	endif
	}
	
/*
=================================================
	переводит float в half
=================================================
*/
	inline void THalf::Set(const float& F)
	{
#	if 0
		// fast conversion

		float32_t	f(F);
		_bits.s = f._bits.s;
		_bits.e = f._bits.e - (127 - 15);
		_bits.m = f._bits.m >> (23-10);
#	else
		// accurate conversion
		// based on code from OpenGL Mathematics (GLM)
		// from http://glm.g-truc.net/

		int i = ReferenceCast<int>(F);
		int s =  (i >> 16) & 0x00008000;
		int e = ((i >> 23) & 0x000000ff) - (127 - 15);
		int m =   i        & 0x007fffff;

		if (e <= 0)
		{
			if (e < -10) {
				_value = 0;
				return;
			}

			m = (m | 0x00800000) >> (1 - e);

			if (m & 0x00001000) 
				m += 0x00002000;

			_value = uint16( s | (m >> 13) );
			return;
		}
		else
		if (e == 0xff - (127 - 15))
		{
			if (m == 0)
			{
				_value = uint16( s | 0x7c00 );
				return;
			}
			else
			{
				m >>= 13;
				_value = uint16( s | 0x7c00 | m | (m == 0) );
				return;
			}
		}
		else
		{
			if (m &  0x00001000)
			{
				m += 0x00002000;

				if (m & 0x00800000)
				{
					m =  0;
					e += 1;
				}
			}

			if (e > 30)
			{
				_value = uint16( s | 0x7c00 );
				return;
			}

			_value = uint16( s | (e << 10) | (m >> 13) );
			return;
		}
#	endif
	}
	

#	undef _HALF_OPERATOR

//-------------------------------------------------------------------

}	// UXTypes
}	// UX_STL

#endif	// _UXS_HALF_TYPE_H